#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <unistd.h>

using namespace std;

int cnt = 0;
int n = 0;


//下面有三种通过系统调用发送信号的方式

//1、kill
// static void Usage(const std::string &proc)//写一个简易版的用户手册，解释如何使用kill
// {
//     std::cout << "\nUsage: " << proc << " -signumber process\n" << std::endl;
// }

// void handler(int signo)//得到的signo参数是signal传来的第一个参数，这个函数可以查看诸如“CTRL+/”对应的信号的编号
// {
//     std::cout << "获得一个" << signo << "号信号" << std::endl;
//     // exit(1);
// }

// // 系统调用kill第一参数为目标进程pid，第二个参数为信号编号，想要模拟实现kill，假设这是输入的指令：./process -9 12345
// 下面使用命令行参数模拟实现
// int main(int argc, char *argv[])
// {
//     if(argc != 3)
//     {
//         Usage(argv[0]);
//         exit(0);
//     }

//     int signumber = std::stoi(argv[1]+1);//加一是为了指令第二个选项可以带“-”
//     int processpid = std::stoi(argv[2]);

//     kill(processpid, signumber);


//     // signal(2, handler);//将数组下标为2的内容修改为handler的地址，也就是修改了指向的内容
//     // signal(19, handler);
//     // signal(20, handler);
//     // signal(3, handler);
//     // signal(9, handler); // 9号信号，不可被自定义捕捉，也就是就算调用signal想要更改对应的内容也不行
//     // while(true)
//     // {
//     //     std::cout << "running...., pid : " << getpid() << std::endl;
//     //     sleep(1);
//     // }
// }

//2、raise
// void handler(int signo)
// {
//     std::cout << "get a signo : " << signo << std::endl;
//     //exit(0);//当这个进程运行起来之后，输入"CTRL+\“才可以终止
// }
// int main()
// {
//     signal(2, handler);

//     while(1)
//     {
//         raise(2);//这个函数可以给自己这个进程发送信号
//         sleep(1);
//     }
//     return 0;
// }

//3、abort
// void handler(int signo)
// {
//     std::cout << "get a signo : " << signo << "alarm : " << cnt  << std::endl;
// }
// int main()
// {
//     signal(SIGABRT, handler);
//     abort();//6号SIGABRT，调用该函数的进程自动终止，即使上一条语句已经修改了其内容，系统依然会在打印语句后终止该进程
//     while(1)
//     {
//         std::cout <<" I am running " << std::endl;
//         sleep(1);
//     }

//     return 0;
// }




// void handler(int signo)
// {
//     std::cout << "get a signo : " << signo << "alarm : " << cnt  << std::endl;
//     sleep(1);
//     exit(0);
// }
// int main()
// {
//     signal(8, handler);//本来8号应该终止进程，但是更改了它的默认动作，但是为什么上面的函数不加上exit（0）会出现死循环呢
//     //CPU内的寄存器属于CPU，但是寄存器内部的内容却属于当前的进程（的硬件上下文）（寄存器 ！= 寄存器内的内容），这里出现异常时
//     //溢出标志位置为1，操作系统接收该信号就会把进程杀死，等其他进程运行时再由其他进程的溢出标志位的数据覆盖之前的内容
//     //也就是说并不会把溢出标志位改为0（0是正常的），这个进程的溢出标志位一直为1
//     //CPU检测到异常就不再执行后续代码，通知操作系统处理异常，操作系统处理方式为打印一条语句，没有终止该进程，等到重新调度该进程的时候
//     //又出现异常，如此循环往复，所以handler函数应该加上exit语句
//     int a = 10;
//     a /= 0;//对应8号信号

//     return 0;
// }


// void handler(int signo)
// {
//     std::cout << "get a signo : " << signo << "alarm : " << cnt  << std::endl;
//     sleep(1);
//     exit(0);
// }
// int main()
// {
//     signal(11, handler);//出现循环的原因同上

//     int *p = nullptr;//指针指向0号地址，但是当前进程的页表并没有建立0号地址到物理内存的映射关系，在页表中查不到相应的映射条目
//     //而这个转化工作由CPU内的MMU（内存管理单元）来执行，当转化失败时MMU也会报错（设置标志位），操作系统得到该信息后就会杀死进程
//     //然后调度下一个进程时，在CPU中再由下一个进程的数据覆盖该进程的数据
//     *p = 100;

//     return 0;
// }


// void handler(int signo)
// {
//     std::cout << "get a signo : " << signo << "，alarm : " << cnt  << std::endl;
//     exit(0);
// }
// int main()
// {
//     signal(14, handler);
//     alarm(2);//14号信号，2秒后向进程发出终止信号

//     while(true)
//     {
//         cnt++;//这条语句只是在内存中一直对cnt做自增操作，因此速度比较快。如果在循环中每次自增都打印一下，那么速度会很慢
//         //因为向显示器打印，显示器是外设，频繁地访问外设会影响效率，说明外设很慢
//     }

//     return 0;
// }



void handler(int signo)
{
    alarm(2);
    // n = alarm(0);//一般来说这个n就是alarm的返回值，也就是上一个闹钟剩下多少时间（可以提前发送kill -9信号查看剩下的时间）
    cout << "result: " << n << endl;
    // exit(0);
}
// void handler(int signo)
// {
//     n = alarm(0);//一般来说这个n就是alarm的返回值，也就是上一个闹钟剩下多少时间（可以把main函数的闹钟的参数设置大一些，然后提前发送kill -9信号查看剩下的时间）
//     cout << "result: " << n << endl;
//     exit(0);
// }
int main()
{
    signal(14, handler);
    cout << "pid: " << getpid() << endl;
    alarm(2);//14号信号，2秒后向进程发出终止信号
    //上面的闹钟一响，就会到handler函数重新设置闹钟，然后往后执行代码，两秒时间到了重新设置一个新的闹钟，也就是每隔两秒输出一次？？？

    while(true)
    {
        sleep(1);
        cout << "running ..." << endl;
    }

    return 0;
}